package com.starsea.book.ui.book.info

import android.app.Application
import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import androidx.collection.arrayMapOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.google.gson.JsonParser
import com.starsea.book.R
import com.starsea.book.base.BaseViewModel
import com.starsea.book.constant.AppConst
import com.starsea.book.constant.AppLog
import com.starsea.book.constant.AppPattern
import com.starsea.book.constant.BookType
import com.starsea.book.constant.EventBus
import com.starsea.book.data.appDb
import com.starsea.book.data.entities.Book
import com.starsea.book.data.entities.BookChapter
import com.starsea.book.data.entities.BookSource
import com.starsea.book.exception.NoBooksDirException
import com.starsea.book.exception.NoStackTraceException
import com.starsea.book.help.AppWebDav
import com.starsea.book.help.book.*
import com.starsea.book.help.coroutine.Coroutine
import com.starsea.book.help.http.newCallResponseBody
import com.starsea.book.help.http.okHttpClient
import com.starsea.book.help.http.postJson
import com.starsea.book.help.http.text
import com.starsea.book.lib.webdav.ObjectNotFoundException
import com.starsea.book.model.BookCover
import com.starsea.book.model.ReadBook
import com.starsea.book.model.analyzeRule.AnalyzeUrl
import com.starsea.book.model.localBook.LocalBook
import com.starsea.book.model.webBook.WebBook
import com.starsea.book.ui.newpage.entity.BaseEntity
import com.starsea.book.ui.newpage.entity.BookInfoEntity
import com.starsea.book.ui.newpage.entity.BookSectionListEntity
import com.starsea.book.ui.newpage.entity.CommentEntity
import com.starsea.book.ui.newpage.utils.gsonList
import com.starsea.book.ui.newpage.utils.gsonNormal
import com.starsea.book.utils.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import okhttp3.RequestBody.Companion.toRequestBody

class BookInfoViewModel(application: Application) : BaseViewModel(application) {
    val bookData = MutableLiveData<Book>()
    val chapterListData = MutableLiveData<List<BookChapter>>()
    val webFiles = mutableListOf<WebFile>()
    var inBookshelf = false
    var bookSource: BookSource? = null
    private var changeSourceCoroutine: Coroutine<*>? = null
    val waitDialogData = MutableLiveData<Boolean>()
    val actionLive = MutableLiveData<String>()

    val bookInfoResult = MutableLiveData<BaseEntity<BookInfoEntity>>()
    val bookSectionListResult = MutableLiveData<BaseEntity<MutableList<BookSectionListEntity>>>()
    val bookCommentResult = MutableLiveData<BaseEntity<String?>>()
    val commentPriseResult = MutableLiveData<BaseEntity<Int>>()
    val bookCommentTotalNumResult = MutableLiveData<BaseEntity<String?>>()
    val addSherfResult = MutableLiveData<BaseEntity<Boolean>>()
    val commentListResult = MutableLiveData<BaseEntity<MutableList<CommentEntity>>>()


    fun priseComment(
        id: String,
        commentType: String = "2",
        oprType: Int = 1,
        mPosition: Int = 0
    ) {
        val url =
            AppConst.BASE_URL.plus("front-api/book_comment/book_comment_like?id=${id}&commentType=${commentType}&oprType=${oprType}")
//        val map: MutableMap<String, String> = arrayMapOf()
//        map["id"] = id
//        map["commentType"] = commentType
//        map["oprType"] = oprType.toString()
//        val json = Gson().toJson(map)
//        LogUtils.e(">>>", ">>.${json}")
        execute {
            okHttpClient.newCallResponseBody {
                url(url)
                    .post("".toRequestBody())
            }.text().let {
                LogUtils.e(">>>", ">>.${it}")
                val baseEntity = BaseEntity<Int>()
                val objects = JsonParser.parseString(it).asJsonObject
                baseEntity.status = objects.get("status").asInt
                baseEntity.msg = objects.get("msg").asString
                baseEntity.data = mPosition
                baseEntity.likeFlag = oprType
                commentPriseResult.postValue(baseEntity)
            }
        }.onError {
            context.toastOnUi(it.localizedMessage ?: "ERROR")
        }
    }

    fun replyComment(
        bookId: String,
        commentType: String = "2",
        commentDesc: String = "2",
        scoreNumber: Float = 10f,
        parentId: String = "",
    ) {
        val url = AppConst.BASE_URL.plus("front-api/book_comment/book_comment_add")
        val map: MutableMap<String, String> = arrayMapOf()
        map["bookId"] = bookId
        map["commentType"] = commentType
        map["commentDesc"] = commentDesc
        map["scoreNumber"] = scoreNumber.toString()
        if (!TextUtils.isEmpty(parentId))
            map["parentId"] = parentId
        val json = Gson().toJson(map)
        LogUtils.e(">>>", ">>.${json}")
        execute {
            okHttpClient.newCallResponseBody {
                url(url)
                    .postJson(json)
            }.text().let {
                LogUtils.e(">>>", ">>.${it}")
                val baseEntity = BaseEntity<String?>()
                val objects = JsonParser.parseString(it).asJsonObject
                baseEntity.status = objects.get("status").asInt
                baseEntity.msg = objects.get("msg").asString
                bookCommentResult.postValue(baseEntity)
            }
        }.onError {
            context.toastOnUi(it.localizedMessage ?: "ERROR")
        }
    }


    //    "bookId": 0,
//    "commentType": 0,
//    "pageNo": 0,
//    "pageSize": 0,
//    "parentId": 0
    fun getCommentList(
        bookId: String,
        commentType: String = "1",
        pageNo: Int = 1,
        pageSize: Int = 10,
        parentId: String = "",
    ) {
        val url = AppConst.BASE_URL.plus("front-api/book_comment/book_comment_list")
        val map: MutableMap<String, String> = arrayMapOf()
        map["bookId"] = bookId
        map["commentType"] = commentType
        map["pageNo"] = pageNo.toString()
        map["pageSize"] = pageSize.toString()
        if (!TextUtils.isEmpty(parentId))
            map["parentId"] = parentId
        val json = Gson().toJson(map)

        execute {
            okHttpClient.newCallResponseBody {
                url(url)
                    .postJson(json)
            }.text().let {
                LogUtils.e(">>>", ">>.${it}")
                val entity: BaseEntity<MutableList<CommentEntity>> =
                    it.gsonList(CommentEntity::class.java)
                commentListResult.postValue(entity)

            }
        }.onError {
            context.toastOnUi(it.localizedMessage ?: "ERROR")
        }
    }


    fun addToShelf(
        bookId: String,
        sectionId: String = "",
        sectionTitle: String = ""
    ) {
        var url = AppConst.BASE_URL.plus("front-api/book-shelf/favorBook?bookId=${bookId}")
        LogUtils.e(">>>>> ", "sectionId = " + sectionId)
        LogUtils.e(">>>>> ", "sectionTitle = " + sectionTitle)
        if (!TextUtils.isEmpty(sectionId)) {
            url = url.plus("&sectionId=${sectionId}&sectionTitle=${sectionTitle}")
        }

        LogUtils.e(">>>>> ", "url = " + url)

        execute {
//            LogUtils.e(">>> ", "json = " + json)
            okHttpClient.newCallResponseBody {
//                LogUtils.e(">>> ", "json = " + json)
                url(url)
                    .post("".toRequestBody())
//                    .postJson(json)

            }.text().let {
                LogUtils.e(">>>", ">>.${it}")
                val entity: BaseEntity<Boolean> =
                    it.gsonNormal(Boolean::class.java)
                addSherfResult.postValue(entity)
            }
        }.onError {
            context.toastOnUi(it.localizedMessage ?: "ERROR")
        }
    }

    /**
     *
     */
    fun getCommentTotalNum(bookId: String) {
        execute {
            okHttpClient.newCallResponseBody {
                url(AppConst.BASE_URL.plus("front-api/book_comment/book_comment_list_count?bookId=${bookId}"))
                    .post("".toRequestBody())
            }.text().let {
                val baseEntity = BaseEntity<String?>()
                val objects = JsonParser.parseString(it).asJsonObject
                baseEntity.status = objects.get("status").asInt
                baseEntity.msg = objects.get("msg").asString
                baseEntity.data = objects.get("data").asString
                bookCommentTotalNumResult.postValue(baseEntity)
            }
        }.onError {
            context.toastOnUi(it.localizedMessage ?: "ERROR")
        }
    }

    /**
     * 获取小说章节列表
     */
    fun getBookSectionListInfo(bookId: String) {
        execute {
            okHttpClient.newCallResponseBody {
                url(AppConst.BASE_URL.plus("front-api/book/book_section_list?bookId=${bookId}"))
                    .post("".toRequestBody())
            }.text().let {

                LogUtils.e(">>>>> ", it)
                val entity: BaseEntity<MutableList<BookSectionListEntity>> =
                    it.gsonList(BookSectionListEntity::class.java)

                val chapterList: MutableList<BookChapter> = arrayListOf()
                entity.data!!.forEachIndexed { index, bookSectionListEntity ->
                    val bookChapter = BookChapter()
                    bookChapter.title = bookSectionListEntity.sectionTitle.toString()
                    bookChapter.url = bookSectionListEntity.sectionId.toString()
//                    是否免费 0:免费 1:收费
//                    bookChapter.isVip = bookSectionListEntity.isFree == 0
                    bookChapter.isPay = bookSectionListEntity.isFree == 0
                    bookChapter.bookUrl = bookId
                    bookChapter.index = index
                    chapterList.add(bookChapter)
                }
                chapterListData.postValue(chapterList)
                bookSectionListResult.postValue(entity)
            }
        }.onError {
            context.toastOnUi(it.localizedMessage ?: "ERROR")
        }
    }

    fun getBookInfo(bookId: String) {
        execute {

            okHttpClient.newCallResponseBody {
                val map: MutableMap<String, String> = arrayMapOf()
                map["bookId"] = bookId
                url(AppConst.BASE_URL.plus("front-api/book/book_read_detail?bookId=${bookId}"))
                    .post("".toRequestBody())
            }.text().let {

                LogUtils.e(">>>>> ", it)
                val entity: BaseEntity<BookInfoEntity> =
                    it.gsonNormal(BookInfoEntity::class.java)

                if (entity.status != 200) {
                    bookInfoResult.postValue(entity)
                } else {
                    val book = Book(
                        name = entity.data!!.bookTitle.toString(),
                        author = entity.data!!.authorName.toString(),
                        kind = "",
                        bookUrl = entity.data!!.bookId.toString(),
                        origin = "",
                        originName = "",
                        type = 1,
                        wordCount = entity.data!!.wordCount.toString(),
                        latestChapterTitle = "",
                        coverUrl = entity.data!!.bookCover.toString(),
                        intro = entity.data!!.bookDesc.toString(),
                        tocUrl = "",
                        originOrder = 0,
                        variable = ""
                    )
//                    是否已加入书架 0 否 1 是
                    inBookshelf = entity.data!!.bookShelfFlag == 1
                    bookData.postValue(book)
                    bookInfoResult.postValue(entity)
                }
            }


        }.onError {
            AppLog.put(it.localizedMessage, it)
            context.toastOnUi(it.localizedMessage)
        }
    }

    fun initData(intent: Intent) {
        execute {
            val bookId = intent.getStringExtra("bookId") ?: ""
            okHttpClient.newCallResponseBody {
                val map: MutableMap<String, String> = arrayMapOf()
                map["bookId"] = bookId
                url(AppConst.BASE_URL.plus("front-api/book/book_read_detail?bookId=${bookId}"))
                    .post("".toRequestBody())
//                get(AppConst.BASE_URL.plus("api/business-service/front-api/book-read/book_read_detail"), map, false)
//          method()
//                post("".toRequestBody())
            }.text().let {

                LogUtils.e(">>>>> ", it)
                val entity: BaseEntity<BookInfoEntity> =
                    it.gsonNormal(BookInfoEntity::class.java)

                if (entity.status != 200) {
                    bookInfoResult.postValue(entity)
                } else {
                    val book = Book(
                        name = entity.data!!.bookTitle.toString(),
                        author = entity.data!!.authorName.toString(),
                        kind = "",
                        bookUrl = entity.data!!.bookId.toString(),
                        origin = "",
                        originName = "",
                        type = 1,
                        wordCount = entity.data!!.wordCount.toString(),
                        latestChapterTitle = "",
                        coverUrl = entity.data!!.bookCover.toString(),
                        intro = entity.data!!.bookDesc.toString(),
                        tocUrl = "",
                        originOrder = 0,
                        variable = ""
                    )
//                    是否已加入书架 0 否 1 是
                    inBookshelf = entity.data!!.bookShelfFlag == 1
                    bookData.postValue(book)
                    bookInfoResult.postValue(entity)
                }
            }

            getBookSectionListInfo(bookId)

        }.onError {
            AppLog.put(it.localizedMessage, it)
            context.toastOnUi(it.localizedMessage)
        }
    }

    fun upBook(intent: Intent) {
        execute {
            val name = intent.getStringExtra("name") ?: ""
            val author = intent.getStringExtra("author") ?: ""
            appDb.bookDao.getBook(name, author)?.let { book ->
                upBook(book)
            }
        }
    }

    private fun upBook(book: Book) {
        execute {
            bookData.postValue(book)
            upCoverByRule(book)
            bookSource = if (book.isLocal) null else
                appDb.bookSourceDao.getBookSource(book.origin)
            if (book.tocUrl.isEmpty() && !book.isLocal) {
                loadBookInfo(book)
            } else {
                val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl)
                if (chapterList.isNotEmpty()) {
                    chapterListData.postValue(chapterList)
                } else {
                    loadChapter(book)
                }
            }
        }
    }

    private fun upCoverByRule(book: Book) {
        execute {
            if (book.coverUrl.isNullOrBlank() && book.customCoverUrl.isNullOrBlank()) {
                BookCover.searchCover(book)?.let { coverUrl ->
                    book.customCoverUrl = coverUrl
                    bookData.postValue(book)
                    if (inBookshelf) {
                        saveBook(book)
                    }
                }
            }
        }
    }

    fun refreshBook(book: Book) {
        execute {
            if (book.isLocal) {
                book.tocUrl = ""
                book.getRemoteUrl()?.let {
                    val bookWebDav = AppWebDav.defaultBookWebDav
                        ?: throw NoStackTraceException("webDav没有配置")
                    val remoteBook = bookWebDav.getRemoteBook(it)
                    if (remoteBook == null) {
                        book.origin = BookType.localTag
                    } else if (remoteBook.lastModify > book.lastCheckTime) {
                        val uri = bookWebDav.downloadRemoteBook(remoteBook)
                        book.bookUrl = if (uri.isContentScheme()) uri.toString() else uri.path!!
                        book.lastCheckTime = remoteBook.lastModify
                    }
                }
            } else {
                val bs = bookSource ?: return@execute
                if (book.originName != bs.bookSourceName) {
                    book.originName = bs.bookSourceName
                }
            }
        }.onError {
            when (it) {
                is ObjectNotFoundException -> {
                    book.origin = BookType.localTag
                }

                else -> {
                    AppLog.put("下载远程书籍<${book.name}>失败", it)
                }
            }
        }.onFinally {
            loadBookInfo(book, false)
        }
    }

    fun loadBookInfo(
        book: Book,
        canReName: Boolean = true,
        scope: CoroutineScope = viewModelScope
    ) {
        execute(scope) {
            if (book.isLocal) {
                loadChapter(book, scope)
            } else {
                bookSource?.let { bookSource ->
                    val oldBook = book.copy()
                    WebBook.getBookInfo(this, bookSource, book, canReName = canReName)
                        .onSuccess(IO) {
                            appDb.bookDao.getBook(book.name, book.author)?.let {
                                inBookshelf = true
                            }
                            bookData.postValue(it)
                            if (inBookshelf) {
                                appDb.bookDao.update(it)
                                if (oldBook.name != book.name) {
                                    BookHelp.updateCacheFolder(oldBook, book)
                                }
                            }
                            if (it.isWebFile) {
                                loadWebFile(it, scope)
                            } else {
                                loadChapter(it, scope)
                            }
                        }.onError {
                            AppLog.put("获取书籍信息失败\n${it.localizedMessage}", it)
                            context.toastOnUi(R.string.error_get_book_info)
                        }
                } ?: let {
                    chapterListData.postValue(emptyList())
                    context.toastOnUi(R.string.error_no_source)
                }
            }
        }
    }

    private fun loadChapter(
        book: Book,
        scope: CoroutineScope = viewModelScope
    ) {
        execute(scope) {
            if (book.isLocal) {
                LocalBook.getChapterList(book).let {
                    appDb.bookDao.update(book)
                    appDb.bookChapterDao.delByBook(book.bookUrl)
                    appDb.bookChapterDao.insert(*it.toTypedArray())
                    chapterListData.postValue(it)
                }
            } else {
                bookSource?.let { bookSource ->
                    val oldBook = book.copy()
                    WebBook.getChapterList(this, bookSource, book, true)
                        .onSuccess(IO) {
                            val dbBook = appDb.bookDao.getBook(book.name, book.author)
                            if (dbBook?.bookUrl == oldBook.bookUrl) {
                                if (oldBook.bookUrl == book.bookUrl) {
                                    appDb.bookDao.update(book)
                                } else {
                                    appDb.bookDao.insert(book)
                                    BookHelp.updateCacheFolder(oldBook, book)
                                }
                                appDb.bookChapterDao.delByBook(oldBook.bookUrl)
                                appDb.bookChapterDao.insert(*it.toTypedArray())
                                if (book.isSameNameAuthor(ReadBook.book)) {
                                    ReadBook.book = book
                                    ReadBook.chapterSize = book.totalChapterNum
                                }
                            }
                            chapterListData.postValue(it)
                        }.onError {
                            chapterListData.postValue(emptyList())
                            AppLog.put("获取目录失败\n${it.localizedMessage}", it)
                            context.toastOnUi(R.string.error_get_chapter_list)
                        }
                } ?: let {
                    chapterListData.postValue(emptyList())
                    context.toastOnUi(R.string.error_no_source)
                }
            }
        }.onError {
            context.toastOnUi("LoadTocError:${it.localizedMessage}")
        }
    }


    fun loadGroup(groupId: Long, success: ((groupNames: String?) -> Unit)) {
        execute {
            appDb.bookGroupDao.getGroupNames(groupId).joinToString(",")
        }.onSuccess {
            success.invoke(it)
        }
    }

    private fun loadWebFile(
        book: Book,
        scope: CoroutineScope = viewModelScope
    ) {
        execute(scope) {
            webFiles.clear()
            val fileNameNoExtension = if (book.author.isBlank()) book.name
            else "${book.name} 作者：${book.author}"
            book.downloadUrls!!.map {
                val analyzeUrl = AnalyzeUrl(it, source = bookSource)
                val mFileName = UrlUtil.getFileName(analyzeUrl)
                    ?: "${fileNameNoExtension}.${analyzeUrl.type}"
                WebFile(it, mFileName)
            }
        }.onError {
            context.toastOnUi("LoadWebFileError\n${it.localizedMessage}")
        }.onSuccess {
            webFiles.addAll(it)
        }
    }

    /* 导入或者下载在线文件 */
    fun <T> importOrDownloadWebFile(webFile: WebFile, success: ((T) -> Unit)?) {
        bookSource ?: return
        execute {
            waitDialogData.postValue(true)
            if (webFile.isSupported) {
                val book = LocalBook.importFileOnLine(
                    webFile.url,
                    bookData.value!!.getExportFileName(webFile.suffix),
                    bookSource
                )
                changeToLocalBook(book)
            } else {
                LocalBook.saveBookFile(
                    webFile.url,
                    bookData.value!!.getExportFileName(webFile.suffix),
                    bookSource
                )
            }
        }.onSuccess {
            @Suppress("unchecked_cast")
            success?.invoke(it as T)
        }.onError {
            when (it) {
                is NoBooksDirException -> actionLive.postValue("selectBooksDir")
                else -> {
                    AppLog.put("ImportWebFileError\n${it.localizedMessage}", it)
                    context.toastOnUi("ImportWebFileError\n${it.localizedMessage}")
                    webFiles.remove(webFile)
                }
            }
        }.onFinally {
            waitDialogData.postValue(false)
        }
    }

    fun getArchiveFilesName(archiveFileUri: Uri, onSuccess: (List<String>) -> Unit) {
        execute {
            ArchiveUtils.getArchiveFilesName(archiveFileUri) {
                AppPattern.bookFileRegex.matches(it)
            }
        }.onError {
            AppLog.put("getArchiveEntriesName Error:\n${it.localizedMessage}", it)
            context.toastOnUi("getArchiveEntriesName Error:\n${it.localizedMessage}")
        }.onSuccess {
            onSuccess.invoke(it)
        }
    }

    fun importArchiveBook(
        archiveFileUri: Uri,
        archiveEntryName: String,
        success: ((Book) -> Unit)? = null
    ) {
        execute {
            val suffix = archiveEntryName.substringAfterLast(".")
            LocalBook.importArchiveFile(
                archiveFileUri,
                bookData.value!!.getExportFileName(suffix)
            ) {
                it.contains(archiveEntryName)
            }.first()
        }.onSuccess {
            val book = changeToLocalBook(it)
            success?.invoke(book)
        }.onError {
            AppLog.put("importArchiveBook Error:\n${it.localizedMessage}", it)
            context.toastOnUi("importArchiveBook Error:\n${it.localizedMessage}")
        }
    }

    fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
        changeSourceCoroutine?.cancel()
        changeSourceCoroutine = execute {
            bookSource = source
            bookData.value?.migrateTo(book, toc)
            if (inBookshelf) {
                book.removeType(BookType.updateError)
                bookData.value?.delete()
                appDb.bookDao.insert(book)
                appDb.bookChapterDao.insert(*toc.toTypedArray())
            }
            bookData.postValue(book)
            chapterListData.postValue(toc)
        }.onFinally {
            postEvent(EventBus.SOURCE_CHANGED, book.bookUrl)
        }
    }

    fun topBook() {
        execute {
            bookData.value?.let { book ->
                val minOrder = appDb.bookDao.minOrder
                book.order = minOrder - 1
                book.durChapterTime = System.currentTimeMillis()
                appDb.bookDao.update(book)
            }
        }
    }

    fun saveBook(book: Book?, success: (() -> Unit)? = null) {

        LogUtils.e(">>>> ", " saveBook = " + book.toString())

        book ?: return

        execute {
            if (book.order == 0) {
                book.order = appDb.bookDao.minOrder - 1
            }
            appDb.bookDao.getBook(book.name, book.author)?.let {
                book.durChapterPos = it.durChapterPos
                book.durChapterTitle = it.durChapterTitle
            }
            book.save()
            if (ReadBook.book?.name == book.name && ReadBook.book?.author == book.author) {
                ReadBook.book = book
            }
            LogUtils.e(">>>> ", " book.save()")
        }.onSuccess {

            LogUtils.e(">>>> ", "onSuccess")
            success?.invoke()
        }
    }

    fun saveChapterList(success: (() -> Unit)?) {
        execute {
            LogUtils.e(">>>> ", "chapterListData  = " + chapterListData.value.toString())
            chapterListData.value?.let {
                appDb.bookChapterDao.insert(*it.toTypedArray())
            }
        }.onSuccess {
            LogUtils.e(">>>> ", "chapterListData  = success ")
            success?.invoke()
        }
    }

    fun addToBookshelf(success: (() -> Unit)?) {
        execute {
            bookData.value?.let { book ->
                if (book.order == 0) {
                    book.order = appDb.bookDao.minOrder - 1
                }
                appDb.bookDao.getBook(book.name, book.author)?.let {
                    book.durChapterPos = it.durChapterPos
                    book.durChapterTitle = it.durChapterTitle
                }
                book.save()
            }
            chapterListData.value?.let {
                appDb.bookChapterDao.insert(*it.toTypedArray())
            }
            inBookshelf = true
        }.onSuccess {
            success?.invoke()
        }
    }

    fun getBook(toastNull: Boolean = true): Book? {
        val book = bookData.value
        if (toastNull && book == null) {
            context.toastOnUi("book is null")
        }
        return book
    }

    fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) {
        execute {
            bookData.value?.let {
                it.delete()
                inBookshelf = false
                if (it.isLocal) {
                    LocalBook.deleteBook(it, deleteOriginal)
                }
            }
        }.onSuccess {
            success?.invoke()
        }
    }

    fun clearCache() {
        execute {
            BookHelp.clearCache(bookData.value!!)
            if (ReadBook.book?.bookUrl == bookData.value!!.bookUrl) {
                ReadBook.clearTextChapter()
            }
        }.onSuccess {
            context.toastOnUi(R.string.clear_cache_success)
        }.onError {
            context.toastOnUi("清理缓存出错\n${it.localizedMessage}")
        }
    }

    fun upEditBook() {
        bookData.value?.let {
            appDb.bookDao.getBook(it.bookUrl)?.let { book ->
                bookData.postValue(book)
            }
        }
    }

    private fun changeToLocalBook(localBook: Book): Book {
        return LocalBook.mergeBook(localBook, bookData.value).let {
            bookData.postValue(it)
            loadChapter(it)
            inBookshelf = true
            it
        }
    }

    data class WebFile(
        val url: String,
        val name: String,
    ) {

        override fun toString(): String {
            return name
        }

        // 后缀
        val suffix: String = UrlUtil.getSuffix(name)

        // txt epub umd pdf等文件
        val isSupported: Boolean = AppPattern.bookFileRegex.matches(name)

        // 压缩包形式的txt epub umd pdf文件
        val isSupportDecompress: Boolean = AppPattern.archiveFileRegex.matches(name)

    }

}
